# coding=utf-8
"""
作者：董新强 
创建时间：
描述：
"""
import time
from datetime import datetime

from sqlalchemy import and_

from auth import PERSON_USER_ROLE
from auth.enum_client import EnumClient
from auth.token_manage import TokenManage
from auth.utils import get_random_chars, get_len, to_string
from frameworks.db_session import DbSession
from frameworks.pbkdf2_password import Pbkdf2Password
from frameworks.redis_helper import ReidsHelper
from frameworks.resonse_msgs import ok, fail
from models.ceping import Users, Persons, UserSmsCode, UserLogin


class UserManage():

    @staticmethod
    def get_user(app_key, phone):
        try:
            with DbSession.create() as db:
                usr = db.query(Users.id,
                               Users.wx_openid,
                               Users.pc_openid,
                               Users.unionid,
                               Users.is_deleted).filter(Users.phone == phone).first()
            if not usr:
                return ok('', app_key=app_key)

            data = {
                'id': usr[0],
                'wx_openid': usr[1],
                'pc_openid': usr[2],
                'unionid': usr[3],
                'is_deleted': usr[4]
            }
            return ok(data, app_key=app_key)
        except Exception as e:
            return fail(hint=str(e))

    @staticmethod
    def create_usr(app_key, phone):
        with DbSession.create() as db:
            usr = db.query(Users.id).filter(Users.phone == phone).first()
            if usr:
                return ok(usr[0], app_key=app_key)

            usr = Users()
            usr.phone = phone
            uid = UserManage._create_user_info(app_key, db, usr)
        return ok(uid, app_key=app_key)

    @staticmethod
    def login_with_wx_open_id(app_key, wx_open_id):
        client = EnumClient.person_mini.value
        with DbSession.create() as db:
            usr = db.query(Users.id, Users.union_id).filter(Users.wx_openid == wx_open_id).first()
            if usr:
                uid, union_id = usr
                rsa_key, expire = TokenManage.save_login_info(db, uid, client)
            else:
                usr = Users()
                usr.wx_openid = wx_open_id
                uid = UserManage._create_user_info(app_key, db, usr)
                rsa_key, expire = TokenManage.save_login_info(db, uid, client)
                union_id = 0
        token = TokenManage.gen_token(uid, expire, rsa_key, client, union_id)
        return ok(data={'uid': uid, 'token': token, 'expire': expire, 'union_id': union_id}, app_key=app_key)

    @staticmethod
    def _create_user_info(app_key, db, usr, pwd=None):
        usr.app = app_key
        pbkdf2p = Pbkdf2Password()
        usr.password = pbkdf2p.encode(get_random_chars(8) if not pwd or get_len(pwd) < 6 else pwd.strip())
        usr.is_superuser = False
        usr.first_name = '沐贤小白'
        usr.last_name = ''
        usr.email = ''
        usr.is_staff = False
        usr.is_active = True
        usr.date_joined = datetime.now()
        usr.is_deleted = False
        usr.headimgurl = ''
        usr.role = PERSON_USER_ROLE
        usr.token = get_random_chars(43)  # 2019-4-25(新增后就不能修改了,否则会出大问题的)
        db.add(usr)  # 添加到数据库
        db.flush()
        usr.username = 'mx-%d' % (usr.id)
        # ----person--------------------
        person = Persons()
        person.name = '沐贤小白'
        person.is_leader = False
        person.user_id = usr.id
        person.is_super_user = False
        person.is_edited = False
        person.role = PERSON_USER_ROLE
        person.is_deleted = False
        person.framework_id = 88  # 默认添加到赋能中心下面
        person.company_id = 1  # 默认添加到 mx下面
        db.add(person)  # 添加到数据库

        return usr.id

    @staticmethod
    def register_with_sms_code(app_key, phone, sms_code, client, pwd=None):
        """
        短信验证码注册用户
        :param app_key: 应用的app_key
        :param phone: 电话号码
        :param sms_code: 短信验证码
        :param pwd: 密码
        :param client: 客户端
        :return:
        """
        if not phone:
            return fail(msg='电话号码格式错误')

        with DbSession.create() as db:
            # 校验短信验证码
            sms_validate_ressult = UserManage.validate(db, phone, sms_code, app_key)
            if not sms_validate_ressult.is_ok():
                return sms_validate_ressult

            # 获取或注册
            usr = db.query(Users.id, Users.union_id).filter(Users.phone == phone).first()
            if usr:
                uid, union_id = usr
            else:
                usr = Users()
                usr.phone = phone
                uid = UserManage._create_user_info(app_key, db, usr, pwd=pwd)
                union_id = 0

            # 登录
            rsa_key, expire = TokenManage.save_login_info(db, uid, client)
        token = TokenManage.gen_token(uid, expire, rsa_key, client, union_id)
        return ok({'uid': uid, 'token': token, 'expire': expire}, app_key=app_key)

    @staticmethod
    def validate(db, phone, code, app_key):
        if get_len(code) != 4:
            return fail(msg='验证错误')

        code = int(code)
        # 先检查user表
        usr = db.query(UserSmsCode.msg_code_send_time, UserSmsCode.msg_code).filter(UserSmsCode.phone == phone).first()

        if not usr:
            return fail(msg='验证码错误')

        send_time, send_code = usr
        if code != send_code:
            return fail(msg='验证码错误')

        if int(time.time()) - send_time > 30 * 60:
            return fail(msg='验证码过期')

        db.query(UserSmsCode).filter(UserSmsCode.phone == phone).delete()
        return ok(None, app_key=app_key)

    @staticmethod
    def reset_pwd(app_key, uid, pwd, client):
        """
        修改用户密码
        :param app_key: 应用app_key
        :param uid: 用户ID
        :param pwd: 新密码
        :param client: 客户端
        :return:
        """
        if get_len(pwd) < 6:
            return fail(msg='密码长度至少为六位')

        with DbSession.create() as db:
            usr = db.query(Users).filter(Users.id == uid).first()
            if not usr:
                return fail(msg='用户不存在')

            usr.password = Pbkdf2Password().encode(pwd)
            rsa_key, expire = TokenManage.save_login_info(db, uid, client)
            union_id = usr.union_id

        token = TokenManage.gen_token(uid, expire, rsa_key, client, union_id)
        return ok(data={'uid': uid, 'token': token, 'expire': expire}, app_key=app_key)

    @staticmethod
    def phone_bind(app_key, uid: int, phone: int, code: int):
        """验证短信验证码"""
        if not phone or not code or code < 1000 or code > 9999: return fail(msg='验证码错误(0)')
        with DbSession.create() as db:
            usr = db.query(UserSmsCode.msg_code_send_time, UserSmsCode.msg_code).filter(UserSmsCode.phone == phone).first()
            if not usr: return fail(msg='验证码错误(1)')

            send_time, send_code = usr
            if code != send_code: return fail(msg='验证码错误(2)')
            if int(time.time()) - send_time > 30 * 60: return fail(msg='验证码过期')

            # 解绑手机
            db.query(Users).filter(Users.phone == str(phone)).filter(Users.id != uid).update({Users.phone: None}, synchronize_session='fetch')
            # 绑定手机
            db.query(Users).filter(Users.id == uid).update({Users.phone: str(phone)})
            # 删除验证码
            db.query(UserSmsCode).filter(UserSmsCode.phone == phone).delete()
            return ok(data={}, app_key=app_key)

    @staticmethod
    def create_empey_user(app_key):
        """
        创建用户并进行登录
        2021年10月13日
        """
        with DbSession.create() as db:
            usr = Users()
            uid = UserManage._create_user_info(app_key, db, usr)
            rsa_key, expire = TokenManage.save_login_info(db, uid, 6)
            union_id = 0
        token = TokenManage.gen_token(uid, expire, rsa_key, 6, union_id)
        return ok({'uid': uid, 'token': token, 'expire': expire}, app_key=app_key)

    @staticmethod
    def change_phone(app_key, uid: int, phone: int):
        """更换手机"""
        with DbSession.create() as db:
            # 解绑手机
            db.query(Users).filter(Users.phone == str(phone)).filter(Users.id != uid).update({Users.phone: None}, synchronize_session='fetch')
            # 绑定手机
            db.query(Users).filter(Users.id == uid).update({Users.phone: str(phone)})
            return ok(data={}, app_key=app_key)

    @staticmethod
    def update_user_info(app_key, uid, req: dict):
        """
        更新用户信息
        """
        allow_fields = {'headimgurl', 'union_id', 'edu', 'birth', 'first_name', 'sex'}
        with DbSession.create() as db:
            # 解绑手机
            usr = db.query(Users).filter(Users.id == uid).first()
            if not usr: return fail(msg='用户不存在')

            for k, v in req.items():
                if not v or k not in allow_fields: continue
                setattr(usr, k, v)

        return ok(data={}, app_key=app_key)

    @staticmethod
    def get_usr_info(app_key, uid):
        """
        获取用户基础信息
        """
        with DbSession.create() as db:
            usr = db.query(
                Users.id,
                Users.headimgurl,
                Users.union_id,
                Users.edu,
                Users.birth,
                Users.first_name,
                Users.sex,
                Users.phone
            ).filter(Users.id == uid).first()
            if not usr: return fail(msg='用户不存在')
            return ok(data={
                'id': usr[0],
                'headimgurl': usr[1],
                'union_id': usr[2],
                'edu': usr[3],
                'birth': to_string(usr[4]),
                'first_name': usr[5],
                'sex': usr[6],
                'phone': usr[7]
            }, app_key=app_key)

    @staticmethod
    def change_union(app_key, uid: int, union_id: int):
        """
        切换用户当前所在工会
        """
        if not uid or not union_id: return fail('参数错误'), None
        with DbSession.create() as db:
            pre_union_id = db.query(Users.union_id).filter(Users.id == uid).first()
            pre_union_id = 0 if not pre_union_id else pre_union_id[0]
            if pre_union_id == union_id: return fail('无法更换到当前'), None  # 和已有有的union_id相同就不更新

            db.query(Users).filter(Users.id == uid).update({
                Users.union_id: union_id
            })

            # 登录过期
            uls = [v[0] for v in db.query(UserLogin.client).filter(UserLogin.uid == uid)]
            if uls:
                for client in uls:
                    db.query(UserLogin).filter(and_(UserLogin.uid == uid, UserLogin.client == client)).update({UserLogin.expire: 0})

        return ok(data={}, app_key=app_key), uls

    @staticmethod
    async def delete_cache_token(uid, clients):
        if not uid or not clients: return
        try:
            cache = ReidsHelper()
            for client in clients:
                await cache.delete('auth_{}_{}'.format(uid, client))
        except:
            pass
